"""这个文件是用来运行它,获取项目中所有的<有name>的URL,并返回"""
import os

if __name__ == "__main__":
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'pear_admin_django.settings')
    import django

    django.setup()

    from django.urls.resolvers import URLResolver, URLPattern
    import re


    def check_url_exclude(url):
        """自定制 排除一些特定的URL"""

        AUTO_DISCOVER_EXCLUDE = [
            '/admin/.*',
            '/rbac/login/',
        ]

        for regex in AUTO_DISCOVER_EXCLUDE:
            if re.match(regex, url):
                return True


    def recursion_url(pre_namespace, pre_url, urlpatterns, url_list):
        """
        用于递归的获取URL
        :param pre_namespace: namespace前缀,以后用于拼接name
        :param pre_url: url前缀,以后用于拼接url
        :param urlpatterns: 路由关系列表
        :param url_list: 用于保存递归中获取的所有路由
        :return:
        """
        for item in urlpatterns:
            """ eg: 根路由下的urlpatterns循环出来的item
            <URLResolver <URLPattern list> (admin:admin) 'admin/'>
            <URLResolver <module 'apps.rbac.urls' from '/Desktop/dc_crm/apps/rbac/urls.py'> (rbac:rbac) 'rbac/'>
            <URLResolver <module 'apps.web.urls' from '/Desktop/dc_crm/apps/web/urls.py'> (None:None) 'web/'>
            <URLPattern 'xxx/'>
            - Django1.x中是RegexURLResolver、RegexURLPattern -- from django.urls import RegexURLResolver, RegexURLPattern
            - Django2.x开始是URLResolver、URLPattern -- from django.urls.resolvers import URLResolver, URLPattern
            """
            if isinstance(item, URLResolver):  # 证明是路由分发,那么需要进行递归操作!!
                # ■ 该语句体里表明,是进行路由分发的路由,该路由肯定不具备name值的,所以只能先对namespace进行处理!!
                # [前面n级路由分发拼接的namespace] [当前的路由分发的namespace] 有 无 -- 排列组合,一共四种情况 Hhh
                if pre_namespace and item.namespace:  # 当前路由分发有namespace和前面的n级路由分发经过迭代后拼接的namespace 都有,再次进行拼接
                    namespace = f"{pre_namespace}:{item.namespace}"
                elif not pre_namespace and item.namespace:  # 前面的n级路由分发都没有namespace,当前路由分发有
                    namespace = item.namespace
                elif pre_namespace and not item.namespace:  # 前面的n级路由分发经过迭代后拼接的namespace 有, 当前路由分发没有
                    namespace = pre_namespace
                else:  # 所有的路由分发都没有namespace
                    namespace = None
                """
                - Django1.x中这么写
                  recursion_urls(namespace, pre_url + item.regex.pattern, item.url_patterns, url_ordered_dict)
                - Django2以上不支持 item.regex
                  item.regex.pattern 应改为 item.pattern.regex.pattern
                """
                # ■ url需要拼接当前路由的url前缀!! Ps: 你无需担心path中的<str:pk>之类的写法,它内部会自动转换为正则的样式!!
                url = pre_url + item.pattern.regex.pattern
                recursion_url(namespace, url, item.url_patterns, url_list)  # <★开始递归>
            # <★其实它就是递归结束的条件,尽管执行它时,return的是None 也证明开始了回溯的过程!!>
            elif isinstance(item, URLPattern):  # 证明是非路由分发,那么需将路由添加到url_ordered_dict字典中!
                if not item.name:  # 该url没有name,那么就不管
                    continue
                if pre_namespace:  # 若该url的上一级进行了路由分发,分发时设置了namespace,那么需要将namespace与当前name进行拼接!
                    name = "%s:%s" % (pre_namespace, item.name)
                else:
                    name = item.name
                # 同理,若该url的上一级进行了路由分发,分发时设置了url前缀,那么需要进行拼接! 前缀 + 当前url
                # 仔细想一想,其实每个上一级都有url前缀,哪怕是根路由,我们也手动传入了 "/" 前缀
                # item.pattern.regex.pattern 可取到当前路由的url, Don't ask me why, 武sir在源码中看到的!
                url = pre_url + item.pattern.regex.pattern
                # eg: /^rbac/^user/edit/(?P<pk>\d+)/$ --> /rbac/user/edit/(?P<pk>\d+)/
                url = url.replace('^', '').replace('$', '')
                # □ 排除的自定制!! 比如排除,admin为前缀的url、login登陆
                if check_url_exclude(url):
                    continue
                url_list.append({"name": name, "url": url})


    def get_all_url_dict():
        """获取项目中所有的<有name>的URL (约定前提:URL必须得有name别名该URL才能被自动发现!!)"""
        # eg: {"rbac:menu_list":{"name":"rbac:menu_list","url":"/rbac/menu/list/"},...,...}
        # 为啥要这么构建数据结构?
        # 1.name值我们在数据库里设置了unique=True,具备唯一性,所有name值可以作为key
        # 2.name还可以做粒度控制到按钮的权限判断
        # So,我们约定俗成,项目中的url必须设置name,不然不会自动找到它!!
        url_list = []
        # settings.ROOT_URLCONF 表明根路由的路径,我们需根据字符串的形式导入该模块
        from pear_admin_django import urls  # 导入urls模块, 其实就是导入settings.ROOT_URLCONF
        md = urls  # 'dc_crm.urls' --> from dc_crm import urls
        # [item for item in md.urlpatterns] # item ==> RegexURLResolver/URLResolver   RegexURLPattern/URLPattern
        # 调用它帮助我们递归的去获取所有路由 注:
        # 1. 根路由是没有上一级的,也就不存在分发,所以一开始pre_namespace值为None
        # 2. 给根路由的url前面都加上"/",所以一开始pre_url值为"/"
        # 3. md.urlpatterns根路由的路由关系列表
        recursion_url(None, "/", md.urlpatterns, url_list)
        return url_list


    res = get_all_url_dict()
    print(res)
